//+---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation
//
// File: Validation.cs
//
// Contents: Object Type Specific validation logic
//
// Created by: Andrew Lin (yunlin) April 6th 2009
//
//----------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
using System.Xml;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.IO;

namespace Microsoft.Reputation.Services
{ 
    public static class TypeSpecificValidation
    {
        internal enum ObjectType
        {
            File = 1,
            Application,
            Publisher,
            Url
        }

        internal class MetadataName
        {
            public const string Sha256 = "SHA256";
            public const string Sha1 = "SHA1";
            public const string Md5 = "MD5";
            public const string Alias = "Alias";
            public const string DisplayName = "Display Name";
            public const string Bloom = "Bloom";
            public const string MsasDataBlock = "DataBlock";
        }

        private static List<ObjectType> s_ValidateObjectTypes = new List<ObjectType>{
            ObjectType.File,
            ObjectType.Application,
            ObjectType.Publisher,
            ObjectType.Url,
        };

        private static List<DataType> s_ValidateDataTypes = new List<DataType>{
            DataType.Metadata,
            DataType.Reputation,
        };

        internal static uint IsObjectTypeValid(uint objectType)
        {
            if (s_ValidateObjectTypes.Contains((ObjectType)objectType))
            {
                return StatusCodes.OK;
            }
            else
            {
                TraceUtility.TraceWarning(
                    Tracing.Source,
                    "Invalid request: requested object type {0} is not valid.",
                    objectType);

                return StatusCodes.InvalidObjectType;
            }
        }

        internal static uint IsLookupFilterValid(Filter filter, uint objectType)
        {
            if (filter == null)
            {
                return StatusCodes.OK;
            }

            if (filter.RequestedData != null && filter.RequestedData.Count > 0)
            {
                foreach (DataType dataType in filter.RequestedData)
                {
                    if (!s_ValidateDataTypes.Contains(dataType))
                    {
                        TraceUtility.TraceWarning(
                            Tracing.Source,
                            "Invalid lookup request: requested data type {0} is not valid.",
                            dataType);

                        return StatusCodes.InvalidDataType;
                    }
                }
            }

            if (filter.RequestedMetadata != null && filter.RequestedMetadata.Count > 0)
            {
                foreach (string metadata in filter.RequestedMetadata)
                {
                    if (metadata == null)
                    {
                        return StatusCodes.InvalidMetadataType;
                    }

                    switch ((ObjectType)objectType)
                    {
                        case ObjectType.Url:


                            if (!metadata.Equals(MetadataName.Sha256, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Equals(MetadataName.Bloom, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: requested URL metadata type {0} is not supported.",
                                    metadata);

                                return StatusCodes.InvalidMetadataType;
                            }

                            break;

                        case ObjectType.File:

                            if (!metadata.Equals(MetadataName.Sha256, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Equals(MetadataName.Sha1, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Equals(MetadataName.Md5, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: requested File metadata type {0} is not supported.",
                                    metadata);

                                return StatusCodes.InvalidMetadataType;
                            }

                            break;

                        case ObjectType.Application:

                            if (!metadata.Equals(MetadataName.DisplayName, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: requested Application metadata type {0} is not supported.",
                                    metadata);

                                return StatusCodes.InvalidMetadataType;
                            }

                            break;

                        case ObjectType.Publisher:

                            if (!metadata.Equals(MetadataName.DisplayName, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Equals(MetadataName.Alias, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: requested Publisher metadata type {0} is not supported.",
                                    metadata);

                                return StatusCodes.InvalidMetadataType;
                            }

                            break;
                    }
                }
            }

            return StatusCodes.OK;
        }

        internal static uint IsListingFilterValid(Filter filter)
        {
            return StatusCodes.OK;
        }

        internal static uint IsSearchableMetadataValid(LookupRequest lookupRequest)
        {
            switch ((ObjectType)lookupRequest.Type)
            {
                case ObjectType.Url:

                    if (lookupRequest.SearchableFields == null || lookupRequest.SearchableFields.Count == 0)
                    {
                        TraceUtility.TraceWarning(
                            Tracing.Source,
                            "Invalid lookup request: lookup URL without searchable field.");

                        return StatusCodes.InvalidSearchableMetadata;
                    }

                    foreach (Metadata metadata in lookupRequest.SearchableFields)
                    {
                        if (!metadata.Name.Equals(MetadataName.MsasDataBlock, StringComparison.InvariantCultureIgnoreCase))
                        {
                            TraceUtility.TraceWarning(
                                Tracing.Source,
                                "Invalid lookup request: lookup URL without DataBlock.");

                            return StatusCodes.InvalidSearchableMetadataName;
                        }

                        byte[] byteArrayCheck = metadata.Value as byte[];
                        if (byteArrayCheck == null)
                        {
                            TraceUtility.TraceWarning(
                                Tracing.Source,
                                "Invalid lookup request: lookup URL with a datablock of type : {0}", metadata.Value.GetType().ToString());
                            return StatusCodes.InvalidTypeForDataBlock;
                        }
                    }

                    break;

                case ObjectType.File:

                    if (lookupRequest.SearchableFields != null && lookupRequest.SearchableFields.Count > 0)
                    {
                        foreach (Metadata metadata in lookupRequest.SearchableFields)
                        {
                            if (!metadata.Name.Equals(MetadataName.Sha256, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Name.Equals(MetadataName.Sha1, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Name.Equals(MetadataName.Md5, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: lookup File without hash.");

                                return StatusCodes.InvalidSearchableMetadataName;
                            }
                        }
                    }

                    break;

                case ObjectType.Application:

                    if (lookupRequest.SearchableFields != null && lookupRequest.SearchableFields.Count > 0)
                    {
                        foreach (Metadata metadata in lookupRequest.SearchableFields)
                        {
                            if (!metadata.Name.Equals(MetadataName.DisplayName, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: lookup Application without Display Name.");

                                return StatusCodes.InvalidSearchableMetadataName;
                            }
                        }
                    }

                    break;

                case ObjectType.Publisher:

                    if (lookupRequest.SearchableFields != null && lookupRequest.SearchableFields.Count > 0)
                    {
                        foreach (Metadata metadata in lookupRequest.SearchableFields)
                        {
                            if (!metadata.Name.Equals(MetadataName.DisplayName, StringComparison.InvariantCultureIgnoreCase) &&
                                !metadata.Name.Equals(MetadataName.Alias, StringComparison.InvariantCultureIgnoreCase))
                            {
                                TraceUtility.TraceWarning(
                                    Tracing.Source,
                                    "Invalid lookup request: lookup Publisher without Display Name or Alias.");

                                return StatusCodes.InvalidSearchableMetadataName;
                            }
                        }
                    }

                    break;
            }

            return StatusCodes.OK;
        }

        internal static uint IsSearchableMetadataValid(ListingRequest listingRequest)
        {
            return StatusCodes.OK;
        }

        internal static uint IsRequestValid(LookupRequest[] lookupRequests, Filter filter)
        {
            uint status, objectType;

            if (lookupRequests == null || lookupRequests.Length == 0)
            {
                return StatusCodes.NullLookupRequest;
            }
            objectType = lookupRequests[0].Type;

            status = IsLookupFilterValid(filter, objectType);

            if (status != StatusCodes.OK)
            {
                return status;
            }

            foreach (LookupRequest request in lookupRequests)
            {
                if (objectType != request.Type)
                {
                    return StatusCodes.InconsistentObjectType;
                }

                status = IsObjectTypeValid(request.Type);

                if (status != StatusCodes.OK)
                {
                    return status;
                }

                status = IsSearchableMetadataValid(request);

                if (status != StatusCodes.OK)
                {
                    return status;
                }
            }

            return StatusCodes.OK;
        }

        internal static uint IsRequestValid(ListingRequest[] listingRequests, Filter filter)
        {
            return StatusCodes.OK;
        }        
    }  
}
